﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using System.Linq;
using PostSharp.CodeModel;
using PostSharp.CodeWeaver;
using PostSharp.Collections;

namespace MicroContainer.CompileTime.Generators
{
   internal class DelegatesCacheInitializerGenerator : ICodeGenerator
   {
      public void Generate(CodeGenerationContext ctx)
      {
         FieldDefDeclaration delegatesCache = ctx.ContainerType.Fields.GetByName("_delegatesCache");
         
         MethodDefDeclaration staticCnstructor = new MethodDefDeclaration();
         ctx.ContainerType.Methods.Add(staticCnstructor);
         staticCnstructor.Attributes = MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName | MethodAttributes.Static;
         staticCnstructor.CallingConvention = CallingConvention.Default;
         staticCnstructor.Name = ".cctor";         
         staticCnstructor.ReturnParameter = new ParameterDeclaration();
         staticCnstructor.ReturnParameter.Attributes = ParameterAttributes.Retval;
         staticCnstructor.ReturnParameter.ParameterType = ctx.ContainerType.Module.FindType(typeof(void), BindingOptions.Default);
         staticCnstructor.MethodBody = new MethodBodyDeclaration();
         staticCnstructor.MethodBody.RootInstructionBlock = staticCnstructor.MethodBody.CreateInstructionBlock();
         //staticCnstructor.MethodBody.MaxStack = 8;

         InstructionBlock block = staticCnstructor.MethodBody.RootInstructionBlock;
         InstructionWriter writer = new InstructionWriter(); 
         InstructionSequence seq = staticCnstructor.MethodBody.CreateInstructionSequence();
         block.AddInstructionSequence(seq, NodePosition.Before, null);         
         writer.AttachInstructionSequence(seq);         
         writer.EmitInstruction(OpCodeNumber.Nop);
         writer.EmitInstructionMethod(OpCodeNumber.Newobj,
                                ctx.ContainerType.Module.FindType(typeof (ArrayList), BindingOptions.Default).
                                   GetTypeDefinition().Methods.GetByName(".ctor").First(def => def.Parameters.Count == 0).Translate(writer.MethodBody.Module));
         writer.EmitInstructionField(OpCodeNumber.Stsfld, delegatesCache);           
         writer.DetachInstructionSequence();
      }
   }
}
